<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>函数声明和调用(Function declaration and invocation)</title>
    <!-- 
  在JavaScript中，函数声明（Function Declaration）和函数调用（Function Invocation）是定义和使用函数的基本概念。

    一、函数声明（Function Declaration）
    函数声明是定义一个函数的语法结构。它由 `function` 关键字、函数名称、参数列表和函数体组成。

    语法：
    function functionName(parameters) {
      // 函数体
    }
    示例：
    function add(a, b) {
      return a + b;
    }
    在上面的示例中， `add` 是函数名称， `a` 和 `b` 是参数。函数体中执行了两个参数的相加操作，并返回结果。

    二、函数调用（Function Invocation）
    函数调用是执行已定义的函数的过程。调用函数时，可以传递参数，并接收返回值。

    语法：
    functionName(arguments);
    示例：
    let result = add(3, 5); // 调用add函数，传入3和5
    console.log(result); // 输出8
    在这个示例中，调用了 `add` 函数，并将 `3` 和 `5` 作为参数传入，返回的结果被存储在 `result` 变量中。

    三、函数的不同调用方式

      1. 普通调用：
        - 直接通过函数名称调用。
        add(2, 3);
      2. 方法调用：
        - 将函数作为对象的方法调用。
        const obj = {
          value: 10,
          getValue: function() {
            return this.value;
          }
        };
        console.log(obj.getValue()); // 输出10
        
      3. 构造函数调用：
        - 使用 `new` 关键字调用函数，创建一个对象实例。
        function Person(name) {
          this.name = name;
        }
        const person1 = new Person("Alice");
        console.log(person1.name); // 输出"Alice"

      4. 立即调用函数表达式（IIFE）：
        - 定义后立即执行的函数。
        (function() {
          console.log("I am an IIFE!");
        })(); // 输出"I am an IIFE!"

    四、参数和返回值
    - 参数：函数可以接受任意数量的参数，参数在函数体内可以像变量一样使用。
    - 返回值：函数可以通过 `return` 语句返回值。如果没有返回值，默认返回 `undefined` 。

    总结
    - 函数声明用于定义函数，包含函数名称、参数和函数体。
    - 函数调用是执行函数的过程，可以传递参数并接收返回值。
    - JavaScript支持多种函数调用方式，包括普通调用、方法调用、构造函数调用和立即调用函数表达式（IIFE）。
    - 理解函数的声明和调用方式是掌握JavaScript编程的基础，有助于编写高效和可维护的代码。
  --></head>
  <body>
    <script>
      // 1. 函数声明和函数调用
      // 1.1 函数声明
      function add(a, b) {
        return a + b;
      }
      // 1.2. 函数调用
      const result = add(3, 5);
      console.log("1.函数声明和函数调用", result); // 输出8

      // 2. 方法调用
      const obj = {
        value: 10,
        getValue: function () {
          return this.value;
        },
      };
      console.log("2.方法调用", obj.getValue()); // 输出10

      // 3. 构造函数调用
      function Person(name) {
        this.name = name;
      }
      const person1 = new Person("Alice");
      console.log("3.构造函数调用", person1.name); // 输出"Alice"

      // 4. 立即调用函数表达式（IIFE）
      (function () {
        console.log("4.立即调用函数表达式", "I am an IIFE!");
      })(); // 输出"I am an IIFE!"

      // 5. 参数和返回值
      function multiply(a, b) {
        return a * b;
      }
      const product = multiply(4, 6);
      console.log("5.参数和返回值", product); // 输出24

      // 6. 函数作为参数
      function applyOperation(a, b, operation) {
        return operation(a, b);
      }
      const sum = applyOperation(3, 5, add);
      console.log("6.函数作为参数", sum); // 输出8

      // 7. 函数作为返回值 - 闭包
      function createMultiplier(multiplier) {
        return function (x) {
          console.log("7.函数作为返回值", multiplier, x);
          return x * multiplier;
        };
      }
      const double = createMultiplier(2);
      console.log("7.函数作为返回值", double(5)); // 输出10

      // 8. 箭头函数
      const square = (x) => x * x;
      console.log("8.箭头函数", square(4)); // 输出16

      // 9. 函数的默认参数
      function greet(name = "World") {
        console.log("9.函数的默认参数", `Hello, ${name}!`);
      }
      greet(); // 输出"Hello, World!"

      // 10. 函数的剩余参数
      function sumAll(...numbers) {
        return numbers.reduce((total, num) => total + num, 0);
      }
      console.log("10.函数的剩余参数", sumAll(1, 2, 3, 4)); // 输出10

      // 11. 函数的展开运算符
      const numbers = [1, 2, 3, 4];
      console.log("11.函数的展开运算符", sumAll(...numbers)); // 输出10

      // 12. 柯里化函数
      function curry(fn) {
        return function curried(...args) {
          if (args.length >= fn.length) {
            return fn.apply(this, args);
          } else {
            return function (...args2) {
              return curried.apply(this, args.concat(args2));
            };
          }
        };
      }
      const addCurried = curry(add);
      console.log("12.柯里化函数", addCurried(1)(2)(3)); // 输出6

      // 13. 闭包
      function createCounter() {
        let count = 0;
        return function () {
          count += 1;
          return count;
        };
      }
      const counter = createCounter();
      console.log("13.闭包", counter()); // 输出1

      // 14. 回调函数
      function fetchData(callback) {
        setTimeout(() => {
          const data = "Hello, World!";
          callback(data);
        }, 1000);
      }
      fetchData((data) => {
        console.log("14.回调函数", data); // 输出"Hello, World!"
      });

      // 15. 递归函数
      function factorial(n) {
        if (n === 0) {
          return 1;
        } else {
          return n * factorial(n - 1);
        }
      }
      console.log("15.递归函数", factorial(5)); // 输出120

      // 16. 高阶函数
      function map(arr, fn) {
        const result = [];
        for (let i = 0; i < arr.length; i++) {
          result.push(fn(arr[i]));
        }
        return result;
      }
      const numbersArr = [1, 2, 3, 4, 5];
      const doubled = map(numbersArr, (num) => num * 2);
      console.log("16.高阶函数", doubled); // 输出[2, 4, 6, 8, 10]
    </script>
  </body>
</html>
